﻿namespace SecutityLib
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// DES Technique
    /// </summary>
    public class DES : EncodingDecodingTech
    {
        /// <summary>
        /// left part (32 bits)
        /// </summary>
        private BitArray left;

        /// <summary>
        /// right part (32 bits)
        /// </summary>
        private BitArray right;

        #region Sbox tables

        /// <summary>
        /// sbox 1
        /// </summary>
        private int[,] s1 = 
        { 
                    { 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7 }, 
                    { 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8 }, 
                    { 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0 }, 
                    { 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 } 
                    };

        /// <summary>
        /// sbox 2
        /// </summary>
        private int[,] s2 =
        { 
                    { 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10 },
                    { 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5 },
                    { 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15 },
                    { 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 } 
                    };

        /// <summary>
        /// sbox 3
        /// </summary>
        private int[,] s3 = 
        { 
                    { 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8 },
                    { 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1 }, 
                    { 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7 }, 
                    { 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 } 
                    };

        /// <summary>
        /// sbox 4
        /// </summary>
        private int[,] s4 =
        { 
                    { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15 }, 
                    { 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9 }, 
                    { 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4 }, 
                    { 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 } 
                    };

        /// <summary>
        /// sbox 5
        /// </summary>
        private int[,] s5 = 
        { 
                    { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9 }, 
                    { 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6 }, 
                    { 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14 }, 
                    { 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 } 
                    };

        /// <summary>
        /// sbox 6
        /// </summary>
        private int[,] s6 = 
        {
                    { 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11 }, 
                    { 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8 }, 
                    { 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6 }, 
                    { 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 } 
                    };

        /// <summary>
        /// sbox 7
        /// </summary>
        private int[,] s7 = 
        { 
                    { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1 },
                    { 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6 },
                    { 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2 }, 
                    { 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 } 
                    };

        /// <summary>
        /// sbox 8
        /// </summary>
        private int[,] s8 =
        {
                    { 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7 },
                    { 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2 },
                    { 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8 }, 
                    { 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 } 
                    };

        #endregion

        #region permutation_tables

        /// <summary>
        /// pc 1 
        /// </summary>
        private int[] pc1Indicies = { 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 };

        /// <summary>
        /// IP Inverse
        /// </summary>
        private int[] initialPermutationInverse = { 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25 };

        /// <summary>
        /// IP values
        /// </summary>
        private int[] initialPermutation = { 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 };

        /// <summary>
        /// expand values
        /// </summary>
        private int[] expandPermutation = { 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1 };

        /// <summary>
        /// PC 2
        /// </summary>
        private int[] pc2Indicies = { 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 };

        /// <summary>
        /// left shifts
        /// </summary>
        private int[] leftShiftValues = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };

        /// <summary>
        /// P values
        /// </summary>
        private int[] permutationP = { 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 };
        #endregion

        /// <summary>
        /// Encodes the specified plain text.
        /// </summary>
        /// <param name="plainText">The plain text.</param>
        /// <param name="key">The key.</param>
        /// <returns>cipher text</returns>
        public override string Encode(string plainText, string key)
        {
            string cipher = string.Empty;
            BitArray plainBits;
            if (plainText[0] == '0' && plainText[1] == 'x')
            {
                plainBits = this.GetBitsOfHexa(plainText);
            }
            else
            {
                plainBits = this.GetBitsOfChar(plainText);
            }

            if (plainBits.Length != 64)
            {
                throw new Exception("plain text must be 64 bits, write them in hexadecimal format");
            }

            BitArray keyBits;

            if (key[0] == '0' && key[1] == 'x')
            {
                keyBits = this.GetBitsOfHexa(key);
            }
            else
            {
                keyBits = this.GetBitsOfChar(key);
            }

            if (keyBits.Length != 64)
            {
                throw new Exception("Key must be 64 bits, write them in hexadecimal format");
            }

            plainBits = this.IPOfPlain(plainBits);
            cipher = this.GetHexaOfBits(plainBits);

            keyBits = this.PC1(keyBits);
            cipher = this.GetHexaOfBits(keyBits);

            BitArray partCKey = this.GetCOfKey(keyBits);
            BitArray partDKey = this.GetDOfKey(keyBits);
            this.left = this.GetLeftOfPlain(plainBits);
            this.right = this.GetRightOfPlain(plainBits);

            for (int i = 0; i < 16; i++)
            {
                partCKey = this.LeftShift(partCKey, i);
                partDKey = this.LeftShift(partDKey, i);
                this.ApplyRound(partCKey, partDKey);
            }

            plainBits = this.Merge(this.left, this.right);
            cipher = this.GetHexaOfBits(plainBits);

            plainBits = this.Swap32(plainBits);
            cipher = this.GetHexaOfBits(plainBits);

            plainBits = this.InverseIPOfPlain(plainBits);

            cipher = this.GetHexaOfBits(plainBits);
            cipher += "\r\n";
            cipher += this.GetCharOfBits(plainBits);

            return cipher;
        }

        /// <summary>
        /// Decodes the specified plain text.
        /// </summary>
        /// <param name="cipherText">The cipher text.</param>
        /// <param name="key">The key.</param>
        /// <returns>
        /// cipher text
        /// </returns>
        public override string Decode(string cipherText, string key)
        {
            string plain = string.Empty;
            BitArray cipherBits;
            if (cipherText[0] == '0' && cipherText[1] == 'x')
            {
                cipherBits = this.GetBitsOfHexa(cipherText);
            }
            else
            {
                cipherBits = this.GetBitsOfChar(cipherText);
            }

            if (cipherBits.Length != 64)
            {
                throw new Exception("Cipher text must be 64 bits, write them in hexadecimal format");
            }

            BitArray keyBits;
            if (key[0] == '0' && key[1] == 'x')
            {
                keyBits = this.GetBitsOfHexa(key);
            }
            else
            {
                keyBits = this.GetBitsOfChar(key);
            }

            if (keyBits.Length != 64)
            {
                throw new Exception("Key must be 64 bits, write them in hexadecimal format");
            }

            cipherBits = this.IPOfPlain(cipherBits);
            keyBits = this.PC1(keyBits);
            BitArray[] partCKeys = new BitArray[17];
            BitArray[] partDKeys = new BitArray[17];

            partCKeys[0] = this.GetCOfKey(keyBits);
            partDKeys[0] = this.GetDOfKey(keyBits);

            this.left = this.GetLeftOfPlain(cipherBits);
            this.right = this.GetRightOfPlain(cipherBits);

            for (int i = 0; i < 16; i++)
            {
                partCKeys[i + 1] = this.LeftShift(partCKeys[i], i);
                partDKeys[i + 1] = this.LeftShift(partDKeys[i], i);
            }

            for (int i = 0; i < 16; i++)
            {
                this.ApplyRound(partCKeys[16 - i], partDKeys[16 - i]);
            }

            cipherBits = this.Merge(this.left, this.right);
            cipherBits = this.Swap32(cipherBits);

            cipherBits = this.InverseIPOfPlain(cipherBits);

            plain = this.GetHexaOfBits(cipherBits);
            plain += "\r\n";
            plain += this.GetCharOfBits(cipherBits);

            return plain;
        }

        /// <summary>
        /// Merges the specified left plain.
        /// </summary>
        /// <param name="leftPlain">The left plain.</param>
        /// <param name="rightPlain">The right plain.</param>
        /// <returns>Merges left and right of the plain or cipher</returns>
        private BitArray Merge(BitArray leftPlain, BitArray rightPlain)
        {
            try
            {
                BitArray plain = new BitArray(64);
                for (int i = 0; i < 32; i++)
                {
                    plain[i] = leftPlain[i];
                }

                for (int i = 0; i < 32; i++)
                {
                    plain[i + 32] = rightPlain[i];
                }

                return plain;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Gets then.
        /// </summary>
        /// <param name="plainBits">The plain bits.</param>
        /// <returns> left of plain</returns>
        private BitArray GetLeftOfPlain(BitArray plainBits)
        {
            try
            {
                BitArray left = new BitArray(32);
                for (int i = 0; i < 32; i++)
                {
                    left[i] = plainBits[i];
                }

                return left;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Gets the right of plain.
        /// </summary>
        /// <param name="plainBits">The plain bits.</param>
        /// <returns>right of plain</returns>
        private BitArray GetRightOfPlain(BitArray plainBits)
        {
            try
            {
                BitArray right = new BitArray(32);
                for (int i = 0; i < 32; i++)
                {
                    right[i] = plainBits[i + 32];
                }

                return right;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Applies the DES round.
        /// </summary>
        /// <param name="partCKey">The c key.</param>
        /// <param name="partDKey">The d key.</param>
        private void ApplyRound(BitArray partCKey, BitArray partDKey)
        {
            try
            {
                BitArray roundKey = this.PC2(partCKey, partDKey);
                BitArray tempLeft = this.left;
                BitArray tempRight = this.right;

                tempRight = this.Expand(tempRight);

                tempRight = this.XOR(tempRight, roundKey);

                tempRight = this.SBOX(tempRight);

                tempRight = this.Permtation(tempRight);

                tempRight = this.XOR(tempRight, tempLeft);

                this.left = this.right;
                this.right = tempRight;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Gets the C of key.
        /// </summary>
        /// <param name="keyBits">The key bits.</param>
        /// <returns>C of the key</returns>
        private BitArray GetCOfKey(BitArray keyBits)
        {
            try
            {
                BitArray partCKey = new BitArray(28);
                for (int i = 0; i < 28; i++)
                {
                    partCKey[i] = keyBits[i];
                }

                return partCKey;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Gets the D of key.
        /// </summary>
        /// <param name="keyBits">The key bits.</param>
        /// <returns>D of the key</returns>
        private BitArray GetDOfKey(BitArray keyBits)
        {
            try
            {
                BitArray partDKey = new BitArray(28);
                for (int i = 0; i < 28; i++)
                {
                    partDKey[i] = keyBits[i + 28];
                }

                return partDKey;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Gets the bits of bytes.
        /// </summary>
        /// <param name="s">The s.</param>
        /// <returns>bits of bytes</returns>
        private BitArray GetBitsOfHexa(string s)
        {
            try
            {
                s = s.Replace(" ", string.Empty);
                s = s.Substring(2);
                BitArray temp = new BitArray(s.Length * 4);
                int cnt = 0;

                for (int i = 0; i < s.Length; i++)
                {
                    char byteChar = s[i];
                    switch (byteChar)
                    {
                        case '0':
                            temp[cnt++] = false;
                            temp[cnt++] = false;
                            temp[cnt++] = false;
                            temp[cnt++] = false;
                            break;
                        case '1':
                            temp[cnt++] = false;
                            temp[cnt++] = false;
                            temp[cnt++] = false;
                            temp[cnt++] = true;
                            break;
                        case '2':
                            temp[cnt++] = false;
                            temp[cnt++] = false;
                            temp[cnt++] = true;
                            temp[cnt++] = false;
                            break;
                        case '3':
                            temp[cnt++] = false;
                            temp[cnt++] = false;
                            temp[cnt++] = true;
                            temp[cnt++] = true;
                            break;
                        case '4':
                            temp[cnt++] = false;
                            temp[cnt++] = true;
                            temp[cnt++] = false;
                            temp[cnt++] = false;
                            break;
                        case '5':
                            temp[cnt++] = false;
                            temp[cnt++] = true;
                            temp[cnt++] = false;
                            temp[cnt++] = true;
                            break;
                        case '6':
                            temp[cnt++] = false;
                            temp[cnt++] = true;
                            temp[cnt++] = true;
                            temp[cnt++] = false;
                            break;
                        case '7':
                            temp[cnt++] = false;
                            temp[cnt++] = true;
                            temp[cnt++] = true;
                            temp[cnt++] = true;
                            break;
                        case '8':
                            temp[cnt++] = true;
                            temp[cnt++] = false;
                            temp[cnt++] = false;
                            temp[cnt++] = false;
                            break;
                        case '9':
                            temp[cnt++] = true;
                            temp[cnt++] = false;
                            temp[cnt++] = false;
                            temp[cnt++] = true;
                            break;
                        case 'a':
                        case 'A':
                            temp[cnt++] = true;
                            temp[cnt++] = false;
                            temp[cnt++] = true;
                            temp[cnt++] = false;
                            break;
                        case 'b':
                        case 'B':
                            temp[cnt++] = true;
                            temp[cnt++] = false;
                            temp[cnt++] = true;
                            temp[cnt++] = true;
                            break;
                        case 'c':
                        case 'C':
                            temp[cnt++] = true;
                            temp[cnt++] = true;
                            temp[cnt++] = false;
                            temp[cnt++] = false;
                            break;
                        case 'd':
                        case 'D':
                            temp[cnt++] = true;
                            temp[cnt++] = true;
                            temp[cnt++] = false;
                            temp[cnt++] = true;
                            break;
                        case 'e':
                        case 'E':
                            temp[cnt++] = true;
                            temp[cnt++] = true;
                            temp[cnt++] = true;
                            temp[cnt++] = false;
                            break;
                        case 'f':
                        case 'F':
                            temp[cnt++] = true;
                            temp[cnt++] = true;
                            temp[cnt++] = true;
                            temp[cnt++] = true;
                            break;
                        default:
                            throw new Exception("String not in hexadecimal format");
                    }
                }

                return temp;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Gets the bits of char.
        /// </summary>
        /// <param name="s">The s.</param>
        /// <returns>bits corresponding to char</returns>
        private BitArray GetBitsOfChar(string s)
        {
            try
            {
                s = s.Replace(" ", string.Empty);

                BitArray temp = new BitArray(s.Length * 8);
                int cnt = 0;
                byte[] byteChar = new byte[1];
                for (int i = 0; i < s.Length; i++)
                {
                    byteChar[0] = (byte)s[i];
                    BitArray t = new BitArray(byteChar);
                    for (int j = 0; j < 8; j++)
                    {
                        temp[cnt++] = t[7 - j];
                    }
                }

                return temp;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Gets the hexa of bits.
        /// </summary>
        /// <param name="temp">The temp.</param>
        /// <returns>hexa of bits</returns>
        private string GetHexaOfBits(BitArray temp)
        {
            try
            {
                string byteString = string.Empty;
                if (temp.Length % 4 != 0)
                {
                    int numberOfExtra = 4 - (temp.Length % 4);
                    BitArray arr = new BitArray(temp.Length + numberOfExtra);
                    for (int i = 0; i < numberOfExtra; i++)
                    {
                        arr[i] = false;
                    }

                    for (int i = numberOfExtra; i < arr.Length; i++)
                    {
                        arr[i] = temp[i - numberOfExtra];
                    }

                    temp = arr;
                }

                for (int i = 0; i < temp.Length; i += 4)
                {
                    if (i != 0 && i % 16 == 0)
                    {
                        byteString += " ";
                    }

                    bool one, two, three, four;
                    one = temp[i];
                    two = temp[i + 1];
                    three = temp[i + 2];
                    four = temp[i + 3];

                    if (!one && !two && !three && !four)
                    {
                        byteString += '0';
                    }
                    else if (!one && !two && !three && four)
                    {
                        byteString += '1';
                    }
                    else if (!one && !two && three && !four)
                    {
                        byteString += '2';
                    }
                    else if (!one && !two && three && four)
                    {
                        byteString += '3';
                    }
                    else if (!one && two && !three && !four)
                    {
                        byteString += '4';
                    }
                    else if (!one && two && !three && four)
                    {
                        byteString += '5';
                    }
                    else if (!one && two && three && !four)
                    {
                        byteString += '6';
                    }
                    else if (!one && two && three && four)
                    {
                        byteString += '7';
                    }
                    else if (one && !two && !three && !four)
                    {
                        byteString += '8';
                    }
                    else if (one && !two && !three && four)
                    {
                        byteString += '9';
                    }
                    else if (one && !two && three && !four)
                    {
                        byteString += 'A';
                    }
                    else if (one && !two && three && four)
                    {
                        byteString += 'B';
                    }
                    else if (one && two && !three && !four)
                    {
                        byteString += 'C';
                    }
                    else if (one && two && !three && four)
                    {
                        byteString += 'D';
                    }
                    else if (one && two && three && !four)
                    {
                        byteString += 'E';
                    }
                    else if (one && two && three && four)
                    {
                        byteString += 'F';
                    }
                    else
                    {
                        throw new Exception("Error Converting to bytes");
                    }
                }

                return byteString;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Gets the hexa of bits.
        /// </summary>
        /// <param name="temp">The temp.</param>
        /// <returns>hexa of bits</returns>
        private string GetCharOfBits(BitArray temp)
        {
            try
            {
                string byteString = string.Empty;
                if (temp.Length % 8 != 0)
                {
                    int numberOfExtra = 4 - (temp.Length % 8);
                    BitArray arr = new BitArray(temp.Length + numberOfExtra);
                    for (int i = 0; i < numberOfExtra; i++)
                    {
                        arr[i] = false;
                    }

                    for (int i = numberOfExtra; i < arr.Length; i++)
                    {
                        arr[i] = temp[i - numberOfExtra];
                    }

                    temp = arr;
                }

                for (int i = 0; i < temp.Length; i += 8)
                {
                    BitArray one = new BitArray(8);
                    one[0] = temp[i];
                    one[1] = temp[i + 1];
                    one[2] = temp[i + 2];
                    one[3] = temp[i + 3];
                    one[4] = temp[i + 4];
                    one[5] = temp[i + 5];
                    one[6] = temp[i + 6];
                    one[7] = temp[i + 7];
                    int value = BinaryConverter.ToNumeral(one, 8);

                    byteString += (char)value;
                }

                return byteString;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// XORs one and two.
        /// </summary>
        /// <param name="one">The one.</param>
        /// <param name="two">The two.</param>
        /// <returns>xor result</returns>
        private BitArray XOR(BitArray one, BitArray two)
        {
            try
            {
                int length1 = one.Length;
                int length2 = two.Length;

                if (length1 != length2)
                {
                    return null;
                }

                BitArray result = new BitArray(length1);

                for (int i = 0; i < length1; i++)
                {
                    result[i] = one[i] ^ two[i];
                }

                return result;
            }
            catch
            {
                throw new Exception("XOR failed");
            }
        }

        /// <summary>
        /// SBOXs the specified bits48.
        /// </summary>
        /// <param name="bits48">The bits48.</param>
        /// <returns>sbox val</returns>
        private BitArray SBOX(BitArray bits48)
        {
            try
            {
                BitArray returned32 = new BitArray(32);
                BitArray temp = new BitArray(6);

                int cntOFBit48 = 0;
                int cntOFBit32 = 0;

                for (int loop = 0; loop < 8; loop++)
                {
                    for (int i = 0; i < 6; i++)
                    {
                        temp[i] = bits48[cntOFBit48];
                        cntOFBit48++;
                    }

                    BitArray row = new BitArray(2);
                    BitArray col = new BitArray(4);

                    row[0] = temp[0];
                    row[1] = temp[5];

                    col[0] = temp[1];
                    col[1] = temp[2];
                    col[2] = temp[3];
                    col[3] = temp[4];

                    int rowNumber = BinaryConverter.ToNumeral(row, 2);
                    int colNumber = BinaryConverter.ToNumeral(col, 4);

                    int stemp = 0;
                    switch (loop)
                    {
                        case 0:
                            stemp = this.s1[rowNumber, colNumber];
                            break;
                        case 1:
                            stemp = this.s2[rowNumber, colNumber];
                            break;
                        case 2:
                            stemp = this.s3[rowNumber, colNumber];
                            break;
                        case 3:
                            stemp = this.s4[rowNumber, colNumber];
                            break;
                        case 4:
                            stemp = this.s5[rowNumber, colNumber];
                            break;
                        case 5:
                            stemp = this.s6[rowNumber, colNumber];
                            break;
                        case 6:
                            stemp = this.s7[rowNumber, colNumber];
                            break;
                        case 7:
                            stemp = this.s8[rowNumber, colNumber];
                            break;
                    }

                    BitArray temps = BinaryConverter.ToBinary(stemp);

                    for (int i = 0; i < 4; i++)
                    {
                        returned32[cntOFBit32] = temps[3 - i];
                        cntOFBit32++;
                    }
                }

                return returned32;
            }
            catch
            {
                throw new Exception("SBOX subsitution failed");
            }
        }

        /// <summary>
        ///  applies PC1
        /// </summary>
        /// <param name="bits64">The bits64.</param>
        /// <returns>pc1 bits</returns>
        private BitArray PC1(BitArray bits64)
        {
            try
            {
                BitArray key56 = new BitArray(56);
                for (int i = 0; i < 56; i++)
                {
                    key56[i] = bits64[this.pc1Indicies[i] - 1];
                }

                return key56;
            }
            catch
            {
                throw new Exception("PC1 failed");
            }
        }

        /// <summary>
        /// IP of plain.
        /// </summary>
        /// <param name="plain64">The plain64.</param>
        /// <returns>IP of plain bits</returns>
        private BitArray IPOfPlain(BitArray plain64)
        {
            try
            {
                BitArray plainNew = new BitArray(64);
                for (int i = 0; i < 64; i++)
                {
                    plainNew[i] = plain64[this.initialPermutation[i] - 1];
                }

                return plainNew;
            }
            catch
            {
                throw new Exception("IP failed");
            }
        }

        /// <summary>
        /// Inverses IP of plain.
        /// </summary>
        /// <param name="plain64">The plain64.</param>
        /// <returns>Inverses IP of plain bits</returns>
        private BitArray InverseIPOfPlain(BitArray plain64)
        {
            try
            {
                BitArray plainNew = new BitArray(64);
                for (int i = 0; i < 64; i++)
                {
                    plainNew[i] = plain64[this.initialPermutationInverse[i] - 1];
                }

                return plainNew;
            }
            catch
            {
                throw new Exception("IP inverse failed");
            }
        }

        /// <summary>
        /// PC 2
        /// </summary>
        /// <param name="keyC">The key C.</param>
        /// <param name="keyD">The key D.</param>
        /// <returns>
        /// PC 2 of bits
        /// </returns>
        private BitArray PC2(BitArray keyC, BitArray keyD)
        {
            try
            {
                BitArray key48 = new BitArray(48);
                for (int i = 0; i < 48; i++)
                {
                    int index = this.pc2Indicies[i] - 1;
                    if (index < 28)
                    {
                        key48[i] = keyC[index];
                    }
                    else
                    {
                        key48[i] = keyD[index - 28];
                    }
                }

                return key48;
            }
            catch
            {
                throw new Exception("PC2 failed");
            }
        }

        /// <summary>
        /// Lefts the shift.
        /// </summary>
        /// <param name="key28">The key28.</param>
        /// <param name="roundNumber">The round number.</param>
        /// <returns>left shifted bits</returns>
        private BitArray LeftShift(BitArray key28, int roundNumber)
        {
            try
            {
                BitArray keyShifted = new BitArray(28);
                int shiftval = this.leftShiftValues[roundNumber];
                for (int i = 0; i < 28; i++)
                {
                    keyShifted[i] = key28[(i + shiftval) % 28];
                }

                return keyShifted;
            }
            catch
            {
                throw new Exception("Left Shift failed");
            }
        }

        /// <summary>
        /// Expands the specified plain32.
        /// </summary>
        /// <param name="plain32">The plain32.</param>
        /// <returns>expanded bits</returns>
        private BitArray Expand(BitArray plain32)
        {
            try
            {
                BitArray plain48 = new BitArray(48);
                for (int i = 0; i < 48; i++)
                {
                    plain48[i] = plain32[this.expandPermutation[i] - 1];
                }

                return plain48;
            }
            catch
            {
                throw new Exception("Expand failed");
            }
        }

        /// <summary>
        /// Permtations the specified plain32.
        /// </summary>
        /// <param name="plain32">The plain32.</param>
        /// <returns>permutes the bits</returns>
        private BitArray Permtation(BitArray plain32)
        {
            try
            {
                BitArray plain32permuted = new BitArray(32);
                for (int i = 0; i < 32; i++)
                {
                    plain32permuted[i] = plain32[this.permutationP[i] - 1];
                }

                return plain32permuted;
            }
            catch
            {
                throw new Exception("permutation failed");
            }
        }

        /// <summary>
        /// Swap32s the specified bits64.
        /// </summary>
        /// <param name="bits64">The bits64.</param>
        /// <returns>32 bit swap</returns>
        private BitArray Swap32(BitArray bits64)
        {
            try
            {
                BitArray returnedVal = new BitArray(64);

                int j = 32;
                for (int i = 0; i < 64; i++)
                {
                    returnedVal[i] = bits64[j % 64];
                    j++;
                }

                return returnedVal;
            }
            catch
            {
                throw new Exception("Swaping failed");
            }
        }
    }
}
